﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Xceed.Silverlight.Data
{
  internal abstract class AsyncParentEnumerator<T> : AsyncEnumerator<T>
  {
    #region Static Fields

    private static readonly Guid CountCallId = DataUtils.CreateCallId();
    private static readonly Guid DataCallId = DataUtils.CreateCallId();
    private static readonly Guid GroupCountCallId = DataUtils.CreateCallId();
    private static readonly Guid StatCallId = DataUtils.CreateCallId();

    #endregion

    #region Constructors

    protected AsyncParentEnumerator( IAsyncEnumerator childEnumerator )
    {
      m_childEnumerator = childEnumerator;

      if( m_childEnumerator != null )
      {
        m_childEnumerator.DataChanged += new EventHandler<DataChangedEventArgs>( this.OnChildDataChanged );
      }
    }

    #endregion

    #region IsFullySynchronized Property

    public override bool IsFullySynchronized
    {
      get
      {
        return this.ChildEnumerator.IsFullySynchronized;
      }
    }

    #endregion

    #region ChildEnumerator Protected Internal Property

    protected internal IAsyncEnumerator ChildEnumerator
    {
      get
      {
        lock( this.SyncRoot )
        {
          return m_childEnumerator;
        }
      }
      set
      {
        lock( this.SyncRoot )
        {
          if( m_childEnumerator != null )
            throw new InvalidOperationException( "An attempt was made to set a ChildEnumerator when one has already been provided." );

          if( m_childEnumerator != value )
          {
            if( m_childEnumerator != null )
            {
              m_childEnumerator.DataChanged -= new EventHandler<DataChangedEventArgs>( this.OnChildDataChanged );
            }

            m_childEnumerator = value;

            if( m_childEnumerator != null )
            {
              m_childEnumerator.DataChanged += new EventHandler<DataChangedEventArgs>( this.OnChildDataChanged );
            }
          }
        }
      }
    }

    private IAsyncEnumerator m_childEnumerator; //null

    #endregion

    #region ChildEnumeratorTyped Protected Internal Property

    protected internal IAsyncEnumerator<T> ChildEnumeratorTyped
    {
      get
      {
        return ( IAsyncEnumerator<T> )this.ChildEnumerator;
      }
    }

    #endregion

    public override IAsyncResult BeginCount( CountParameters parameters, AsyncCallback userCallback, object asyncState )
    {
      IAsyncEnumerator enumerator = this.ChildEnumerator;
      AsyncResult asyncResult = new AsyncResult( AsyncParentEnumerator<T>.CountCallId, userCallback, asyncState );

      asyncResult.Start(
        ( cb, st ) => enumerator.BeginCount( parameters, cb, st ),
        ( par, iar ) => new DependentCallResult( enumerator.EndCount( iar ) ) );

      return asyncResult;
    }

    public override CountResult EndCount( IAsyncResult asyncResult )
    {
      asyncResult.ValidateCallEnd( AsyncParentEnumerator<T>.CountCallId );

      return asyncResult.GetCallResult<CountResult>();
    }

    public override IAsyncResult BeginGroupCount( CountParameters parameters, AsyncCallback userCallback, object asyncState )
    {
      IAsyncEnumerator enumerator = this.ChildEnumerator;
      AsyncResult asyncResult = new AsyncResult( AsyncParentEnumerator<T>.GroupCountCallId, userCallback, asyncState );

      asyncResult.Start(
        ( cb, st ) => enumerator.BeginGroupCount( parameters, cb, st ),
        ( par, iar ) => new DependentCallResult( enumerator.EndGroupCount( iar ) ) );

      return asyncResult;
    }

    public override GroupCountResult EndGroupCount( IAsyncResult asyncResult )
    {
      asyncResult.ValidateCallEnd( AsyncParentEnumerator<T>.GroupCountCallId );

      return asyncResult.GetCallResult<GroupCountResult>();
    }

    public override JumpMoveAndFetchAsyncResult BeginJumpMoveAndFetch( JumpMoveAndFetchParameters parameters, AsyncCallback userCallback, object asyncState )
    {
      IAsyncEnumerator<T> enumerator = this.ChildEnumeratorTyped;
      JumpMoveAndFetchAsyncResult asyncResult = new JumpMoveAndFetchAsyncResult( AsyncParentEnumerator<T>.DataCallId, userCallback, asyncState, parameters );

      asyncResult.Start(
        ( cb, st ) => enumerator.BeginJumpMoveAndFetch( parameters, cb, st ),
        ( par, iar ) => new DependentCallResult( enumerator.EndJumpMoveAndFetch( ( JumpMoveAndFetchAsyncResult )iar ) ) );

      return asyncResult;
    }

    public override JumpMoveAndFetchResult<T> EndJumpMoveAndFetch( JumpMoveAndFetchAsyncResult jumpMoveAndFetchAsyncResult )
    {
      jumpMoveAndFetchAsyncResult.ValidateCallEnd( AsyncParentEnumerator<T>.DataCallId );

      return jumpMoveAndFetchAsyncResult.GetCallResult<JumpMoveAndFetchResult<T>>();
    }

    public override IAsyncResult BeginGetStatValues( StatParameters parameters, AsyncCallback userCallback, object asyncState )
    {
      IAsyncEnumerator enumerator = this.ChildEnumerator;
      AsyncResult asyncResult = new AsyncResult( AsyncParentEnumerator<T>.StatCallId, userCallback, asyncState );

      asyncResult.Start(
        ( cb, st ) => enumerator.BeginGetStatValues( parameters, cb, st ),
        ( par, iar ) => new DependentCallResult( enumerator.EndGetStatValues( iar ) ) );

      return asyncResult;
    }

    public override StatResults EndGetStatValues( IAsyncResult asyncResult )
    {
      asyncResult.ValidateCallEnd( AsyncParentEnumerator<T>.StatCallId );

      return asyncResult.GetCallResult<StatResults>();
    }

    protected virtual void ChildDataChanged( DataChangedEventArgs e )
    {
      this.RaiseDataChanged( e );
    }

    protected override IEnumerable<DataSourceChangedEventArgs> TransformDataSourceChangedEventArgs(
      DataSourceContext context,
      DataSourceChangedEventArgs e )
    {
      lock( this.SyncRoot )
      {
        if( m_disposed )
          return Enumerable.Empty<DataSourceChangedEventArgs>();

        return this.ChildEnumerator.TransformDataSourceChangedEventArgs( context, e );
      }
    }

    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if( m_childEnumerator != null )
        {
          m_childEnumerator.DataChanged -= new EventHandler<DataChangedEventArgs>( this.OnChildDataChanged );
          m_childEnumerator.Dispose();
        }
      }

      m_disposed = true;

      base.Dispose( disposing );
    }

    private void OnChildDataChanged( object sender, DataChangedEventArgs e )
    {
      if( !m_disposed )
      {
        this.ChildDataChanged( e );
      }
    }

    #region Private Fields

    private bool m_disposed; //false

    #endregion
  }
}
